package cn.comtom.ewbs.signature.service.impl;

import java.util.List;
import java.util.Map;

import javax.xml.bind.DatatypeConverter;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;

import com.google.common.collect.Maps;

import cn.comtom.ewbs.signature.common.exception.SignException;
import cn.comtom.ewbs.signature.common.utils.ConfigFileUtil;
import cn.comtom.ewbs.signature.common.vo.request.CertApplyReq;
import cn.comtom.ewbs.signature.common.vo.response.CertResp;
import cn.comtom.ewbs.signature.service.AbstractSignService;
import cn.tass.exceptions.YJException;
import cn.tass.yingjgb.YingJGBCALLDLL;
import lombok.extern.slf4j.Slf4j;

/**
 * 签名生成和验签
 * 
 * @author liuhy
 *
 */
@Slf4j
@Service("sjj1303")
public class Sjj1303ServiceImpl extends AbstractSignService implements InitializingBean, DisposableBean{

    // 连接设备的类型标识  0 ：代表SJJ1507密码机 , 1:代表SJJ1303密码器
 	public static final Integer flag = 1;
	 	
	/**
	 * 生成签名
	 * 
	 * @param xmlFile
	 * @return String
	 */
	@Override
	public String getByteSign(byte[] data) {
		String xmlSign = "";
		synchronized (YingJGBCALLDLL.class) {
			try {
				xmlSign = YingJGBCALLDLL.platformCalculateSignature(dataType, data);
				log.info("use sjj1303 getByteSign:{}",xmlSign);
			} catch (YJException e) {
				log.error("use sjj1303 getByteSign failed.", e);
				return null;
			}
			xmlSign = DatatypeConverter.printHexBinary(DatatypeConverter.parseBase64Binary(xmlSign));
			return xmlSign;
		}
	}

	/**
	 * 验证签名
	 * 
	 * @return boolean
	 */
	@Override
	public Boolean verifyByteSign(byte[] data, String signValue) throws YJException {
		boolean result = false;
		signValue = DatatypeConverter.printBase64Binary(DatatypeConverter.parseHexBinary(signValue));
		synchronized (YingJGBCALLDLL.class) {
			try {
				result = YingJGBCALLDLL.platformVerifySignature(dataType, data, signValue);
				log.info("use sjj1303 verifyByteSign:" + result);
			} catch (YJException e) {
				log.error("use sjj1303 verifyByteSign failed.", e);
			}
			return result;
		}
	}
	
	/**
	 * 打开密码机
	 * 
	 */
	@Override
	public boolean openDevice() {
		try {
			// 打开sjj1303密码机
			synchronized (YingJGBCALLDLL.class) {
				YingJGBCALLDLL.openDevice(flag);
				return true;
			}
		} catch (Exception e) {
			log.error("open device sjj1303 failed {}", e);
			throw new SignException(String.format("打开sjj1303设备失败-%s", e.getMessage()));
		}
	}

	/**
	 * 关闭密码机
	 */
	@Override
	public void closeDevice() {
		try {
			YingJGBCALLDLL.closeDevice();
		} catch (Exception e) {
			log.error("close device sjj1303 failed.", e);
			e.printStackTrace();
		}
	}

	@Override
	public CertResp certApply(CertApplyReq certApplyReq) {
		
		return null;
	}

	@Override
	public String getFileSign(byte[] data) {
		String xmlSign = "";
		synchronized (YingJGBCALLDLL.class) {
			try {
				xmlSign = YingJGBCALLDLL.platformCalculateSignature(dataType, data);
				log.info("use sjj1303 getFileSign:{}",xmlSign);
			} catch (YJException e) {
				log.error("use sjj1303 getFileSign failed.", e);
				return null;
			}
			return xmlSign;
		}
	}

	@Override
	public Boolean verifyFileSign(byte[] data, String signValue) throws YJException {
		boolean result = false;
		synchronized (YingJGBCALLDLL.class) {
			// 打开密码机
			// 验证数据签名
			try {
				result = YingJGBCALLDLL.platformVerifySignature(dataType, data, signValue);
				log.info("use sjj1303 verifyFileSign :" + result);
			} catch (YJException e) {
				log.error("use sjj1303 verifyFileSign failed.", e);
			}
			return result;
		}
	}
	
	@Override
	public Boolean importCert(List<String> certs) {
		log.info("importCert use:{}",signType);
		boolean result = true;
		try {
			for (String cert : certs) {
				byte[] certData = aschex_to_bcdhex(cert.getBytes());
				result = YingJGBCALLDLL.platformImportTrustedCert(certData);
				if(!result) {
					log.error("importCert failed! certStr:{}",cert);
					result = false;	
					continue;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	@Override
	public Boolean importCertauthList(String certauthList) {
		log.info("importCertauthList use:{}",signType);
		boolean result = true;
		try {
			byte[] certAuth = aschex_to_bcdhex(certauthList.getBytes());
			result = YingJGBCALLDLL.platformImportTrustedCertSnChain(certAuth);
			if (!result) {
				log.error("importCertauthList failed! CertAuth:{}",certauthList);
				result = false;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	@Override
	public void destroy() throws Exception {
		closeDevice();
		
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		//openDevice();
	}
	
	/**
	 * 十六进制字符串转为字节数组
	 * @param aschexByte
	 * @return
	 */
	private byte[] aschex_to_bcdhex(byte[] aschexByte) {
		int j = 0;
		if (aschexByte.length % 2 == 0) {
			j = aschexByte.length / 2;
			byte[] resTmp = new byte[j];
			for (int i = 0; i < j; i++) {
				resTmp[i] = ascToHex(aschexByte[2 * i], aschexByte[2 * i + 1]);
			}
			return resTmp;

		} else {
			j = aschexByte.length / 2 + 1;
			byte[] resTmp = new byte[j];
			for (int i = 0; i < j - 1; i++) {
				resTmp[i] = ascToHex((byte) aschexByte[2 * i],
						(byte) aschexByte[2 * i + 1]);
			}
			resTmp[j - 1] = ascToHex((byte) aschexByte[2 * (j - 1)], (byte) 0);
			return resTmp;
		}
	}

	private byte ascToHex(byte ch1, byte ch2) {
		byte ch;
		if (ch1 >= 'A')
			ch = (byte) ((ch1 - 0x37) << 4);
		else
			ch = (byte) ((ch1 - '0') << 4);
		if (ch2 >= 'A')
			ch |= (byte) (ch2 - 0x37);
		else
			ch |= (byte) (ch2 - '0');
		return ch;
	}
	
	@Override
	public boolean updateConfig(String configFilePath, String ip, Integer port, String signNo, String logPath, Integer conLimit, Integer timeOut) {
		Map<String,Map<String,String>> configFileMap = Maps.newHashMap();
		Map<String, String> configItemMap1 = Maps.newHashMap();
		configItemMap1.put("logPath", logPath);
		configFileMap.put("LOGGER", configItemMap1);
		
		Map<String, String> configItemMap2 = Maps.newHashMap();
		configItemMap2.put("host", ip);
		configItemMap2.put("port", port.toString());
		configItemMap2.put("timeout", timeOut.toString());
		configItemMap2.put("linkNum", conLimit.toString());
		configFileMap.put("HOST 1", configItemMap2);
		
		return ConfigFileUtil.updateIniFile(configFilePath, configFileMap);
	}
}
